Formius.Element_Abstract = $.inherit({

	// protected properties
	_events: {},
	_form: null,
	_label: null,
	_name: null,
	_parent: null,
	_validators: {
		enable: null,
		validator: null
	},

	// public properties
	element: null,
	options: {
		placeholder: null,
		enableInvert: false
	},

	__constructor: function(element, parent) {
		this.setElement(element);
		this.setParent(parent);
		if (typeof(this.getElement().attr('onclick')) == 'function') {
			var options = this.getElement().attr('onclick')();
			if (!Formius.Helper.isObject(options)) {
    			options = {};
			}
			this._parseOptions(options);
		}
		this.trigger(this.__self.EVENT_INIT);
		this.getElement().bind('change', {element: this}, this.__changeHandler);
		if (!this.isContainer()) {
			this.bind('change', function() {
				if (this.getParent().showChildErrors()) {
					this.getParent().validate();
					this.getParent().showMessage();
				} else {
					this.validate();
					this.showMessage();
				}
			});
		}
	},

	// public methods

	/**
	* @param Validator_Abstract validator
	* @param String logig
	* @param Boolean breakAfter
	* @return Void
	*/
	addValidator: function(validator, logic, breakAfter, context) {
		logic = logic || this.__self.LOGIC_AND;
		breakAfter = !!breakAfter;
		if (!validator) {
			return;
		}
		if (!this.hasValidator(context)) {
			this.__setValidator(new Formius.Validator_Container(), context);
		}
		this.getValidator(context).addValidator(validator, logic, breakAfter, context);
	},
	bind: function(event, func) {
		event = '' + event || '';
		if (event != '' && func instanceof Function) {
			if (!(event in this._events) || !$.isArray(this._events[event])) {
				this._events[event] = [];
			}
			if (!Formius.Helper.inArray(func)) {
				this._events[event].push(func);
			}
		}
		return this;
	},
	destroy: function() {
		if (!this.isForm()) {
			this.getParent().remove(this);
		}
	},
	getCode: function() {
		if (this.hasValidator()) {
			return this.getValidator().getCode() || [];
		}
		return [];
	},
	getContainer: function() {
		var container = this.getElement().parents(this.getContainerSelector());
		if (container.size() <= 0) {
			container = this.getElement().parent();
		}
		return container;
	},
	getContainerSelector: function() {
		return this.getForm().getSettings().selectors.field;
	},
	getElement: function() {
		return this.element;
	},
	getErrorTemplate: function() {
		return this.getForm().getSettings().errors.field;
	},
	getForm: function() {
		return this._form;
	},
	getLabel: function() {
		if (this._label === null) {
			var label = this.getContainer().find(this.getForm().getSettings().selectors.label);
			if (label.size() == 1) {
				this._label = label.text();
			}
		}
		return this._label;
	},
	/**
	 * @return Array
	 */
	getMessage: function() {
		if (this.hasValidator()) {
			return this.getValidator().getMessage() || [];
		}
		return [];
	},
	getName: function() {
		if (this._name === null) {
			this._name = this._getName();
		}
		return this._name;
	},
	getOption: function(optionName) {
		if (optionName in this.options) {
			return this.options[optionName];
		}
		return null;
	},
	getParent: function() {
		return this._parent;
	},
	getValidator: function(context) {
		context = context || 'validator';
		return this._validators[context] || null;
	},
	getValue: function() {
		return this.getElement().val() || '';
	},
	hasValidator: function(context) {
		return this.getValidator(context) !== null;
	},
	init: function() {
		return this;
	},
	isEnabled: function() {
		if (!this.hasValidator('enable')) {
			return true;
		}
		var enabled = this.getValidator('enable').isValid(this.getValue());
		if (this.options.enableInvert) {
			enabled = !enabled;
		}
		return enabled;
	},
	isForm: function() {
		return false;
	},
	isContainer: function() {
		return false;
	},
	render: function() {
		return this;
	},
	resetErrors: function() {
		for (var i in this._validators) {
			if ('reset' in this._validators[i] && this._validators[i].reset instanceof Function) {
				this._validators[i].reset();
			}
		}
		return this;
	},
	setClasses: function(clear) {
		var fieldContainer = this.getContainer(),
			errorClass = this.getForm().getSettings().classes.error || 'error';
		clear = clear || false;
		if (clear) {
			fieldContainer.removeClass(errorClass);
		} else {
			fieldContainer.addClass(errorClass);
		}
		return this;
	},
	setElement: function(element) {
		this.element = $(element);
		this.element.addClass('f-id' + $.identify(this.element));
		return this;
	},
	setForm: function(form) {
		form = form || null;
		if (form !== null && 'isForm' in form && form.isForm()) {
			this._form = form;
		}
		return this;
	},
	setName: function(name) {
		this._name = name;
		return this;
	},
	setParent: function(parent) {
		if (!('isContainer' in parent && 'isForm' in parent)) {
			return this;
		}
		if (parent.isContainer()) {
			this._parent = parent;
			if (parent.isForm()) {
				this.setForm(parent);
			} else {
				this.setForm(parent.getForm());
			}
		}
		return this;
	},
	setValue: function(value) {
		this.getElement().val(value);
		return this;
	},
	showMessage: function() {
		var messages = this.getMessage(),
			fieldContainer = this.getContainer(), i, len;

		fieldContainer.find(this.getForm().getSettings().selectors.error).remove();
		if (messages.length > 0) {
			this.setClasses((this.getMessage(false).length <= 0));
			for (i = 0, len = messages.length; i < len; i++) {
				fieldContainer.append($(Formius.Helper.replace('{message}', messages[i], this.getErrorTemplate())));
			}
		} else {
			this.setClasses(true);
		}
		return this;
	},
	trigger: function(eventType, args) {
		args = $.isArray(args)? args: [];
		if (eventType in this._events && $.isArray(this._events[eventType])) {
			for (var i = this._events[eventType].length - 1; i >= 0; i--) {
				if (this._events[eventType][i] instanceof Function) {
					this._events[eventType][i].apply(this, args);
				}
			}
		}
		return this;
	},
	validate: function() {
		var res = true;
		this.trigger(this.__self.EVENT_PREVALIDATE);
		if (this.isEnabled()) {
			if (this.hasValidator()) {
				res = this.getValidator().isValid(this.getValue());
			}
		} else {
			this.resetErrors();
		}
		this.trigger(this.__self.EVENT_VALIDATE);
		return res;
	},
	unbind: function(event, func) {
		var i;
		event = '' + event || '';
		func = func || null;
		if (event != '') {
			if (event in this._events && $.isArray(this._events[event])) {
				if (func instanceof Function && (i = Formius.Helper.inArray(func, this._events[event], true)) >= 0) {
					this._events[event][i] = null;
					this._events[event].splice(i, 1);
				} else {
					this._events[event] = [];
				}
			}
		}
		return this;
	},

	// protected methods

	_getName: function() {
		var name = this.getElement().attr('name') || this.getOption('name') || '';
		return name != ''? name: null;
	},
	/**
	 * @param Object options
	 * @return Void
	 **/
	_parseOptions: function(options) {
		if (!options) {
			return this;
		}
		if ('validators' in options && Formius.Helper.isObject(options.validators)) {
			this.__createValidators('validator', options.validators);
		}
		if ('enable' in options && Formius.Helper.isObject(options.enable)) {
			this.__createValidators('enable', options.enable);
		}
		this.options = Formius.Helper.extend(this.options, options.settings);
		return this;
	},

	// private methods
	__createValidators: function(context, options) {
		var validator;
		for(var ns in options) {
			if (options[ns].isValid instanceof Function) {
				validator = options[ns];
			} else {
				validator = Formius.Validator_Factory.create('Validator_' + ns, options[ns], this.getParent());
			}
			if (validator !== null) {
				this.addValidator(validator, null, null, context);
			}
		}
		return this;
	},
	__change: function() {
		this.trigger(this.__self.EVENT_CHANGE);
		if (!this.isForm()) {
			this.getParent().__change();
		}
	},
	__changeHandler: function(e) {
		try {
			e.data.element.__change();
		} catch (e) {}
	},
	__setValidator: function(validator, context) {
		context = context || 'validator';
		if (typeof this._validators[context] != 'undefined') {
			this._validators[context] = validator;
		}
		return this;
	}
}, {
	LOGIC_AND: 'and',
	LOGIC_OR: 'or',

	EVENT_CHANGE: 'change',
	EVENT_INIT: 'init',
	EVENT_PREVALIDATE: 'prevalidate',
	EVENT_VALIDATE: 'validate'
});